名域 ts {
    接口 统计_ {
        名称: 文字;
        值: 文字;
    }

    函数 行数量(程序: 程序_): 数字 {
        变量 数量 = 0;
        循环执行(程序.获取源文件组(), 文件 => {
            数量 += 获取行开始(文件).长度;
        });
        返回 数量;
    }

    函数 获取诊断文本(_消息: 诊断信息_, ..._实参组: 任意[]): 文字 {
        常量 诊断 = 创建编译器诊断.应用(未定, arguments);
        返回 <文字>诊断.消息文本;
    }

    变量 报告诊断信息 = 创建诊断报告(sys);
    函数 更新报告诊断信息(选项: 编译选项_) {
        如果 (选项.彩色) {
            报告诊断信息 = 创建诊断报告(sys, /*pretty*/ 为真);
        }
    }

    函数 填充左侧(s: 文字, 长度: 数字) {
        判断 (s.长度 < 长度) {
            s = ' ' + s;
        }
        返回 s;
    }

    函数 填充右侧(s: 文字, 长度: 数字) {
        判断 (s.长度 < 长度) {
            s = s + ' ';
        }

        返回 s;
    }

    导出 函数 执行命令行(实参值: 文字[]): 无值 {
        常量 命令行 = 解析命令行(实参值);
        变量 配置文件名称 = 正规化路径(合并路径(sys.获取当前目录(), 'klang.json'));
        变量 项目配置文件 = 正规化路径(合并路径(sys.获取当前目录(), 'klang.mod.json'));

        如果 (命令行.错误!.长度 > 0) {
            命令行.错误!.循环执行(报告诊断信息);
            返回 sys.退出(退出状态_.诊断当前_已跳过输出_);
        }

        如果 (命令行.选项组!.初始) {
            如果 (!sys.文件存在(配置文件名称)) {
                写默认编译选项文件();
            }
            如果(!sys.文件存在(项目配置文件)){
                写项目配置文件模板();
            }
            返回 sys.退出(退出状态_.成功_);
        }

        如果 (命令行.选项组!.版本) {
            打印版本();
            返回 sys.退出(退出状态_.成功_);
        }

        如果 (命令行.选项组!.帮助) {
            打印版本();
            打印帮助();
            返回 sys.退出(退出状态_.成功_);
        }
        常量 命令行选项组 = 命令行.选项组;
        如果 (配置文件名称) {
            常量 配置分析结果 = 解析配置文件包括系统(配置文件名称, sys, 报告诊断信息);
            更新报告诊断信息(配置分析结果!.选项组!);
            如果 (是设置监控(配置分析结果!.选项组!)) {
                报告监控模式无系统支持();
                创建配置文件的监控(配置分析结果!);
            }
            否则 {
                执行编译(配置分析结果!.文件名组!, 配置分析结果!.选项组!);
            }
        }
        否则 {
            更新报告诊断信息(命令行选项组!);
            如果 (是设置监控(命令行选项组!)) {
                报告监控模式无系统支持();
                创建文件和编译选项监控(命令行.文件名组!, 命令行选项组!);
            }
            否则 {
                执行编译(命令行.文件名组!, 命令行选项组!);
            }
        }
    }

    函数 报告监控模式无系统支持() {
        如果 (!sys.监控文件 || !sys.监控目录) {
            报告诊断信息(创建编译器诊断(诊断信息_.当前解析主机不支持_0_选项, '-监控'));
            sys.退出(退出状态_.诊断当前_已跳过输出_);
        }
    }

    函数 执行编译(根文件名称组: 文字[], 编译选项: 编译选项_) {
        常量 编译主机 = 创建编译主机(编译选项);
        启用统计信息(编译选项);

        常量 程序 = 创建程序(根文件名称组, 编译选项, 编译主机);
        常量 退出代码 = 输出文件和报告错误(程序, 报告诊断信息, s => sys.写出(s + sys.新行));
        报告统计信息(程序);
        返回 sys.退出(退出代码);
    }

    函数 更新监控编译主机(监控编译主机: 监控编译主机_<输出和可视化语义诊断构建程序_>) {
        常量 编译使用构建器 = 监控编译主机.创建程序;
        监控编译主机.创建程序 = (根名称组, 选项, 主机, 旧程序) => {
            启用统计信息(选项);
            返回 编译使用构建器(根名称组, 选项, 主机, 旧程序);
        };
        常量 输出文件使用构建器 = 监控编译主机.程序创建之后!;
        监控编译主机.程序创建之后 = 构建程序 => {
            输出文件使用构建器(构建程序);
            报告统计信息(构建程序.获取程序());
        };
    }

    函数 创建监控状态报告(编译选项: 编译选项_) {
        返回 ts.创建监控状态报告(sys, !!编译选项.彩色);
    }

    函数 创建配置文件的监控(配置解析结果: 解析命令行_) {
        常量 监控编译主机 = 创建配置文件的监控编译主机(配置解析结果.选项组!.配置文件路径! 转为 文字, sys, /*createProgram*/ 未定, 报告诊断信息, 创建监控状态报告(配置解析结果.选项组!));
        更新监控编译主机(监控编译主机);
        监控编译主机.根文件组 = 配置解析结果.文件名组;
        监控编译主机.选项组 = 配置解析结果.选项组;
        创建监控程序(监控编译主机);
    }

    函数 创建文件和编译选项监控(根文件组: 文字[], 选项组: 编译选项_) {
        常量 监控编译主机 = 创建文件和编译选项的监控编译主机(根文件组, 选项组, sys, /*createProgram*/ 未定, 报告诊断信息, 创建监控状态报告(选项组));
        更新监控编译主机(监控编译主机);
        创建监控程序(监控编译主机);
    }

    函数 启用统计信息(编译选项: 编译选项_) {
        如果 (编译选项.诊断信息 || 编译选项.扩展诊断信息) {
            性能.启用性能();
        }
    }

    函数 报告统计信息(程序: 程序_) {
        变量 统计: 统计_[];
        常量 编译选项 = 程序.获取编译选项();
        如果 (编译选项.诊断信息 || 编译选项.扩展诊断信息) {
            统计 = [];
            常量 内存使用 = sys.获取内存使用 ? sys.获取内存使用() : -1;
            报告计数统计('文件数', 程序.获取源文件组().长度);
            报告计数统计('行数', 行数量(程序));
            报告计数统计('节点数', 程序.获取节点数量());
            报告计数统计('标识符数', 程序.获取标识符数量());
            报告计数统计('符号数', 程序.获取符号数量());
            报告计数统计('类型数', 程序.获取类型数量());

            如果 (内存使用 >= 0) {
                报告统计值('内存使用', 数学_.round(内存使用 / 1000) + 'K');
            }

            常量 编译时间 = 性能.获取持续时间('程序');
            常量 绑定时间 = 性能.获取持续时间('绑定');
            常量 检查事件 = 性能.获取持续时间('检查');
            常量 输出事件 = 性能.获取持续时间('输出');
            如果 (编译选项.扩展诊断信息) {
                性能.循环测量((名称, 持续时间) => 报告时间统计(`${名称} 时间`, 持续时间));
            }
            否则 {
                // Individual component times.
                // Note: To match the behavior of previous versions of the compiler, the reported parse time includes
                // I/O read time and processing time for triple-斜杠号_ references and module imports, and the reported
                // emit time includes I/O write time. We preserve this behavior so we can accurately compare times.
                报告时间统计('I/O 读', 性能.获取持续时间('I/O 读'));
                报告时间统计('I/O 写', 性能.获取持续时间('I/O 写'));
                报告时间统计('编译时间', 编译时间);
                报告时间统计('绑定时间', 绑定时间);
                报告时间统计('检查时间', 检查事件);
                报告时间统计('输出时间', 输出事件);
            }
            报告时间统计('总用时', 编译时间 + 绑定时间 + 检查事件 + 输出事件);
            报告统计();

            性能.禁用性能();
        }

        函数 报告统计() {
            变量 名称大小 = 0;
            变量 值大小 = 0;
            循环 (常量 { 名称, 值 } 属于 统计) {
                如果 (名称.长度 > 名称大小) {
                    名称大小 = 名称.长度;
                }

                如果 (值.长度 > 值大小) {
                    值大小 = 值.长度;
                }
            }

            循环 (常量 { 名称, 值 } 属于 统计) {
                sys.写出(填充右侧(名称 + ':', 名称大小 + 2) + 填充左侧(值.转为文字(), 值大小) + sys.新行);
            }
        }

        函数 报告统计值(名称: 文字, 值: 文字) {
            统计.压入({ 名称, 值: 值 });
        }

        函数 报告计数统计(名称: 文字, 数量: 数字) {
            报告统计值(名称, '' + 数量);
        }

        函数 报告时间统计(名称: 文字, 时间: 数字) {
            报告统计值(名称, (时间 / 1000).转为四舍五入(2) + 's');
        }
    }

    函数 打印版本() {
        sys.写出(获取诊断文本(诊断信息_.版本_0, 版本号) + sys.新行);
    }

    函数 打印帮助(显示全部选项: 真假 = 为真) {
        常量 输出: 文字[] = [];

        // We want to align our 'syntax' and 'examples' commands to a certain margin.
        常量 语法长度 = 获取诊断文本(诊断信息_.Syntax_Colon_0, '').长度;
        常量 示例长度 = 获取诊断文本(诊断信息_.Examples_Colon_0, '').长度;
        变量 边距长度 = 数学_.max(语法长度, 示例长度);

        // Build up the syntactic skeleton.
        变量 句法 = 制作边距(边距长度 - 语法长度);
        句法 += 'klang [' + 获取诊断文本(诊断信息_.选项) + '] [' + 获取诊断文本(诊断信息_.文件) + ' ...]';

        输出.压入(获取诊断文本(诊断信息_.Syntax_Colon_0, 句法));
        输出.压入(sys.新行 + sys.新行);

        // Build up the 列表 of examples.
        常量 边距 = 制作边距(边距长度);
        输出.压入(获取诊断文本(诊断信息_.Examples_Colon_0, 制作边距(边距长度 - 示例长度) + 'klang  hello.k') + sys.新行);
        输出.压入(边距 + 'klang -o 输出的文件.a  输入的文件.k' + sys.新行);
        输出.压入(边距 + 'klang @参数.txt' + sys.新行);
        输出.压入(sys.新行);

        输出.压入(获取诊断文本(诊断信息_.选项_Colon) + sys.新行);

        // Sort our options by their names, (e.g. '--noImplicitAny' comes before '--watch')
        常量 选项列表 = 显示全部选项 ?
            排序(选项声明组, (a, b) => 边距文字不区分大小写(a.名称, b.名称)) :
            过滤器(选项声明组.分切(), v => v.显示在简化帮助视图!);

        // We want our descriptions to align at the same column in our output,
        // so we keep track of the longest option usage string.
        边距长度 = 0;
        常量 使用列: 文字[] = []; // Things like '-d, --declaration' go in here.
        常量 说明列: 文字[] = [];

        常量 选项描述词典 = 创建词典<文字[]>();  // Map between option.description and 列表 of option.类型 if it is a 种类

        循环 (常量 选项 属于 选项列表) {
            // If an option lacks a description,
            // it is not officially supported.
            如果 (!选项.描述) {
                继续;
            }

            变量 使用文本 = ' ';
            如果 (选项.短名) {
                使用文本 += '-' + 选项.短名;
                使用文本 += 获取参数类型(选项);
                使用文本 += ', ';
            }

            使用文本 += '--' + 选项.名称;
            使用文本 += 获取参数类型(选项);

            使用列.压入(使用文本);
            变量 描述 = 获取诊断文本(选项.描述);
            说明列.压入(描述);

            // Set the new margin for the description column if necessary.
            边距长度 = 数学_.max(使用文本.长度, 边距长度);
        }

        // Special case that can't fit in the loop.
        常量 使用的文本 = ' @<' + 获取诊断文本(诊断信息_.文件) + '>';
        使用列.压入(使用的文本);
        说明列.压入(获取诊断文本(诊断信息_.Insert_command_line_options_and_files_from_a_file));
        边距长度 = 数学_.max(使用的文本.长度, 边距长度);

        // Print out each row, aligning all the descriptions on the same column.
        循环 (变量 i = 0; i < 使用列.长度; i++) {
            常量 使用的 = 使用列[i];
            常量 描述 = 说明列[i];
            常量 种类列表 = 选项描述词典.获取(描述);
            输出.压入(使用的 + 制作边距(边距长度 - 使用的.长度 + 2) + 描述 + sys.新行);

            如果 (种类列表) {
                输出.压入(制作边距(边距长度 + 4));
                循环 (常量 种类 属于 种类列表) {
                    输出.压入(种类 + ' ');
                }
                输出.压入(sys.新行);
            }
        }

        循环 (常量 行 属于 输出) {
            sys.写出(行);
        }
        返回;

        函数 获取参数类型(选项: 命令行选项_) {
            如果 (选项.参数类型 !== 未定) {
                返回 ' ' + 获取诊断文本(选项.参数类型);
            }
            返回 '';
        }

        函数 制作边距(边距长度: 数字): 文字 {
            返回 数组_(边距长度 + 1).连结(' ');
        }
    }
    函数 写项目配置文件模板() {
        常量 当前目录 = sys.获取当前目录();
        常量 程序配置文件 = 正规化路径(合并路径(当前目录, 'klang.mod.json'));
        如果 (sys.文件存在(程序配置文件)) {
            报告诊断信息(创建编译器诊断(诊断信息_.这个目录下具有一个_0_文件, 'klang.mod.json'));
        }
        否则 {
            sys.写文件(程序配置文件, 生成KLang_mod());
            报告诊断信息(创建编译器诊断(诊断信息_.成功创建一个_0_文件,  'klang.mod.json'));
        }
        返回;
    }

    函数 写默认编译选项文件() {
        常量 当前目录 = sys.获取当前目录();
        常量 编译选项配置文件 = 正规化路径(合并路径(当前目录, 'klang.json'));
        如果 (sys.文件存在(编译选项配置文件)) {
            报告诊断信息(创建编译器诊断(诊断信息_.这个目录下具有一个_0_文件, 'klang.json'));
        }
        否则 {
            sys.写文件(编译选项配置文件, 生成KLang());
            报告诊断信息(创建编译器诊断(诊断信息_.成功创建一个_0_文件, 'klang.json' ));
        }

        返回;
    }
}

如果 (ts.调试_.是调试中_) {
    ts.调试_.启用调试信息();
}

如果 (ts.sys.尝试启用源映射从主机 && /^development$/i.测试(ts.sys.获取环境变量('NODE_ENV'))) {
    ts.sys.尝试启用源映射从主机();
}

如果 (ts.sys.设置非阻塞) {
    ts.sys.设置非阻塞();
}

ts.执行命令行(ts.sys.实参组);
